#ifndef __QTCH_SERVLET_H__
#define __QTCH_SERVLET_H__

#include <memory>
#include <functional>
#include <map>
#include <unordered_map>
#include <vector>
#include "http.h"
#include "http_session.h"
#include "qtch/mutex.h"


namespace qtch{
namespace http{

class Servlet {
public:
    typedef std::shared_ptr<Servlet> ptr;
    Servlet(const std::string& name)
        :m_name(name){}
    virtual ~Servlet(){}
    virtual int32_t handle(HttpRequest::ptr request
                    , HttpResponse::ptr response
                    , HttpSession::ptr session) = 0;
    const std::string& getName() const {return m_name;}

protected:
    std::string m_name;
};


class FunctionServlet : public Servlet {
public:
    typedef std::shared_ptr<FunctionServlet> ptr;
    typedef std::function<int32_t (qtch::http::HttpRequest::ptr request
                    , qtch::http::HttpResponse::ptr response
                    , qtch::http::HttpSession::ptr session)> callback;
    
    virtual int32_t handle(HttpRequest::ptr request
                    , HttpResponse::ptr response
                    , HttpSession::ptr session) override;
    FunctionServlet(callback cb);

private:
    callback m_cb;
};

class IServletCreator {
public:
    typedef std::shared_ptr<IServletCreator> ptr;
    virtual ~IServletCreator(){}
    virtual Servlet::ptr get() const = 0;
    virtual std::string getName() const = 0;
};

class HoldServletCreator : public IServletCreator {
public:
    typedef std::shared_ptr<HoldServletCreator> ptr;
    HoldServletCreator(Servlet::ptr servlet)
        :m_servlet(servlet){}
    virtual Servlet::ptr get() const override {
        return m_servlet;
    }
    virtual std::string getName() const override {
        return m_servlet->getName();
    }
private:
    Servlet::ptr m_servlet;
};

template<class T>
class ServletCreator : public IServletCreator {
public:
    typedef std::shared_ptr<ServletCreator> ptr;
    ServletCreator(){}
    virtual Servlet::ptr get() const override {
        return Servlet::ptr(new T);
    }
    virtual std::string getName() const override {
        return TypeToName<T>();
    }
};

class ServletDispath : public Servlet {
public:
    typedef std::shared_ptr<ServletDispath> ptr;
    typedef RWMutex RWMutexType;
    ServletDispath();
    virtual ~ServletDispath();
    virtual int32_t handle(HttpRequest::ptr request
                    , HttpResponse::ptr response
                    , HttpSession::ptr session) override;
    void addServlet(const std::string& uri, Servlet::ptr slt);
    void addServlet(const std::string& uri, FunctionServlet::callback cb);
    void addGlobServlet(const std::string& uri, Servlet::ptr slt);
    void addGlobServlet(const std::string& uri, FunctionServlet::callback cb);

    void addServlet(const std::string& uri, IServletCreator::ptr creator);
    void addGlobServlet(const std::string& uri, IServletCreator::ptr creator);

    template<class T>
    void addServletCreator(const std::string& uri){
        addServlet(uri,std::make_shared<ServletCreator<T> >());
    }

    template<class T>
    void addGlobServletCreator(const std::string& uri){
        addGlobServlet(uri,std::make_shared<ServletCreator<T> >());
    }

    void delServlet(const std::string& uri);
    void delGlobServlet(const std::string& uri);

    Servlet::ptr getDefault() const {return m_default;}
    void setDefault(Servlet::ptr def) { m_default = def;}

    Servlet::ptr getServlet(const std::string& uri);
    Servlet::ptr getGlobServlet(const std::string& uri);
    virtual Servlet::ptr getMatchedServlet(const std::string& uri);

    void listAllServletCreator(std::unordered_map<std::string, IServletCreator::ptr>& infos);
    void listAllGlobServletCreator(std::unordered_map<std::string, IServletCreator::ptr>& infos);
protected:
    RWMutexType m_mutex;
    std::unordered_map<std::string, IServletCreator::ptr> m_datas;
    std::vector<std::pair<std::string, IServletCreator::ptr> > m_globs;
    
    Servlet::ptr m_default;
};

class NotFoundServlet : public Servlet {
public:
    typedef std::shared_ptr<NotFoundServlet> ptr;
    NotFoundServlet(const std::string& name);
    virtual int32_t handle(HttpRequest::ptr request
                    , HttpResponse::ptr response
                    , HttpSession::ptr session) override;

private:
    std::string m_name;
    std::string m_content;
};

}
}



#endif